package maxbet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/astaxie/beego/logs"

	"lottery/models"
	"lottery/services/lottery"
	"lottery/utils"
)

type MaxBet struct {
	token        string
	userAgent    string
	serverDomain string
	agentInfo    *models.Agent
	jar          *cookiejar.Jar
	client       *http.Client
}

func (m *MaxBet) New(agentInfo *models.Agent) lottery.ILottery {
	client := new(http.Client)
	jar, _ := cookiejar.New(nil)
	client.Jar = jar
	return &MaxBet{
		userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36",
		agentInfo: agentInfo,
		jar:       jar,
		client:    client,
	}
}

func (m *MaxBet) Name() string {
	return "maxbet"
}

func (m *MaxBet) AgentInfo() *models.Agent {
	return m.agentInfo
}

func (m *MaxBet) getInitEnv(reqUrl, passwd string) (
	htmlData []byte,
	cookies []*http.Cookie,
	passwdEncrypt string,
	err error) {
	htmlData, cookies, passwdEncrypt, err = utils.MaxBetLoginEnv(reqUrl, passwd)
	return
}

func (m *MaxBet) login() (resp *models.ActionResp) {
	reqUrl := "https://www.maxbet.com/Default.aspx?IsSSL=1&hidSelLang=cs"
	passwd := m.agentInfo.Passwd

	htmlData, cookies, passwdEncrypt, err := m.getInitEnv(reqUrl, passwd)
	if err != nil {
		return m.errActionResp(err)
	}

	domain, _ := url.Parse(reqUrl)
	if domain != nil {
		// set cookie
		m.jar.SetCookies(domain, cookies)
	}

	buf := bytes.NewBuffer(htmlData)
	doc, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		return m.errActionResp(err)
	}

	txtCode, _ := doc.Find("#txtCode").Attr("value")
	detecas, _ := doc.Find("#detecas-analysis").Attr("value")
	tk, _ := doc.Find("#__tk").Attr("value")
	di, _ := doc.Find("#__di").Attr("value")

	// 登录参数
	params := make(url.Values)
	params.Add("txtID", m.agentInfo.Name)
	params.Add("RMME", "on")
	params.Add("txtPW2", m.agentInfo.Passwd)
	params.Add("txtPW", passwdEncrypt)
	params.Add("txtCode", txtCode)
	params.Add("hidKey", "")
	params.Add("hidLowerCasePW", "")
	params.Add("IEVerison", "0")
	params.Add("hidServerKey", "maxbet.com")
	params.Add("detecResTime", "")
	params.Add("detecas-analysis:", detecas)
	params.Add("__tk", tk)
	params.Add("IsSSL", "1")
	params.Add("PF", "Default")
	params.Add("__di", di)

	// 登录头
	loginUrl := "https://www.maxbet.com/ProcessLogin.aspx"
	httpReq, _ := http.NewRequest("POST", loginUrl, strings.NewReader(params.Encode()))
	header := m.getDefaultHeader()
	header.Set("Referer", reqUrl)
	header.Set("Content-Type", "application/x-www-form-urlencoded")
	httpReq.Header = header

	// 登录
	httpResp, err := m.client.Do(httpReq)
	if err != nil {
		return m.errActionResp(err)
	}

	data, _ := ioutil.ReadAll(httpResp.Body)
	httpResp.Body.Close()

	// 验证部分
	reg, _ := regexp.Compile("'[^']*'")
	verifyUrl := reg.FindString(string(data))
	verifyUrl = strings.Replace(verifyUrl, "'", "", -1)
	if !strings.Contains(verifyUrl, "GetLoginVerifyInfo") {
		return &models.ActionResp{IsSuccess: false, Msg: verifyUrl}
	}

	verifyUrl = path.Join("www.maxbet.com", verifyUrl)
	verifyUrl = "https://" + verifyUrl
	if resp := m.verifyLogin(loginUrl, verifyUrl); resp != nil {
		return resp
	}

	if err := m.setBalance(); err != nil {
		if err := m.setBalance(); err != nil {
			return m.errActionResp(err)
		}
	}

	if err := m.setAccountInfo(); err != nil {
		if err := m.setAccountInfo(); err != nil {
			return m.errActionResp(err)
		}
	}
	return &models.ActionResp{IsSuccess: true}
}

func (m *MaxBet) Login() (actionResp *models.ActionResp) {
	actionResp = m.login()
	if actionResp.IsSuccess {
		go m.heartbeat()
	}

	return
}

func (m *MaxBet) heartbeat() {
	tick := time.NewTicker(time.Second * 3)
	for t := range tick.C {
		logs.Notice("maxbet user %s heartbeat %s", m.agentInfo.Name, t.String())
		if err := m.sendHeartbeat(); err != nil {
			logs.Warn("maxbet %s send heartbeat err:%s", m.agentInfo.Name, err.Error())
			break
		}
	}
	return
}

func (m *MaxBet) sendHeartbeat() (err error) {
	liveUrl := fmt.Sprintf("https://%s/Streaming/LiveList", m.serverDomain)
	httpReq, _ := http.NewRequest("POST", liveUrl, nil)
	header := m.getDefaultHeader()
	header.Set("Referer", fmt.Sprintf("https://%s/sports", m.serverDomain))
	httpReq.Header = header

	httpResp, err := m.client.Do(httpReq)
	if err != nil {
		return
	}
	defer httpResp.Body.Close()

	data, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return
	}

	heartbeat := &struct {
		ErrorCode int `json:"ErrorCode"`
	}{}

	if err = json.Unmarshal(data, &heartbeat); err != nil {
		return
	}

	if heartbeat.ErrorCode != 0 {
		return fmt.Errorf("heartbeat check err: %s", string(data))
	}
	return nil
}

func (m *MaxBet) verifyLogin(lastUrl, verifyUrl string) *models.ActionResp {
	logs.Debug("%s 登录认证 %s", m.agentInfo.Name, verifyUrl)
	httpReq, _ := http.NewRequest("GET", verifyUrl, nil)
	header := m.getDefaultHeader()
	header.Set("Referer", lastUrl)
	httpReq.Header = header

	httpResp, err := m.client.Do(httpReq)
	if err != nil {
		return m.errActionResp(err)
	}
	httpResp.Body.Close()

	logs.Debug("%s 进入首页 %s", m.agentInfo.Name, httpResp.Request.URL.String())
	m.setServerDomain(httpResp.Request.URL.Host)
	return nil
}

func (m *MaxBet) setServerDomain(domain string) {
	logs.Debug("%s 使用服务器 %s", m.agentInfo.Name, domain)
	m.serverDomain = domain
}

func (m *MaxBet) tttt() {
	cli, err := gosocketio.Dial(
		"wss://agnj3.maxbet.com/socket.io/?gid=56d691b510d0567d&token=1d53c161-8d1b-4f16-8b5d-aa2a6cecf71b&id=26507937&rid=0&EIO=3&transport=websocket&sid=LZoOyMrosb4WF3qgBLcf",
		transport.GetDefaultWebsocketTransport(),
	)
	if err != nil {
		panic(err)
	}

	if err = cli.On(gosocketio.OnConnection, func(h *gosocketio.Channel) {
		log.Println("Connected")
	}); err != nil {
		log.Println(err.Error())
	}

	if err = cli.On(gosocketio.OnDisconnection, func(h *gosocketio.Channel) {
		log.Fatal("Disconnected")
	}); err != nil {
		log.Println(err.Error())
	}

	if err = cli.On("p", func(h *gosocketio.Channel) {
		log.Println(h.Id())
	}); err != nil {
		log.Println(err.Error())
	}

	if err = cli.On("r", func(h *gosocketio.Channel) {
		log.Println(h.Id())
	}); err != nil {
		log.Println(err.Error())
	}

	time.Sleep(time.Minute * 10)
	cli.Close()
}

func (m *MaxBet) setAccountInfo() (err error) {
	// 账号信息，以及请求相关的参数设置
	logs.Debug("设置账号信息 %s", m.agentInfo.Name)

	reqUrl := fmt.Sprintf("https://%s", m.serverDomain)
	httpReq, _ := http.NewRequest("GET", reqUrl, nil)
	header := m.getDefaultHeader()
	header.Set("Referer", "https://"+m.serverDomain+"/sports")
	httpReq.Header = header
	resp, err := m.client.Do(httpReq)
	if err != nil {
		logs.Warn("setAccountInfo do err:%s", err.Error())
		return
	}

	doc, err := goquery.NewDocumentFromResponse(resp)
	if err != nil {
		logs.Warn("setAccountInfo readAll err:%s", err.Error())
		return
	}

	var infoJs string
	doc.Find("head > script").Each(func(index int, sel *goquery.Selection) {
		if len(infoJs) > 0 {
			return
		}

		ret := sel.Text()

		if strings.Contains(ret, "homepageodds") {
			infoJs = ret
		}
	})

	compile, _ := regexp.Compile("\"tk\":\"[^\"]*\"")
	token := compile.FindString(infoJs)
	token = strings.Replace(token, "\"tk\":", "", 1)
	token = strings.Replace(token, "\"", "", 2)
	m.token = token
	return
}

func (m *MaxBet) setBalance() (err error) {
	logs.Debug("设置用户信息 %s", m.agentInfo.Name)
	reqUrl := fmt.Sprintf("https://%s/Customer/Balance", m.serverDomain)
	httpReq, _ := http.NewRequest("POST", reqUrl, nil)
	header := m.getDefaultHeader()
	header.Set("Referer", "https://"+m.serverDomain+"/sports")
	httpReq.Header = header
	resp, err := m.client.Do(httpReq)
	if err != nil {
		logs.Warn("setBalance do err:%s", err.Error())
		return
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logs.Warn("setBalance readAll err:%s", err.Error())
		return
	}

	balanceResp := new(BalanceResp)
	if err = json.Unmarshal(data, balanceResp); err != nil {
		logs.Warn("setBalance unmarshal data:%s err:%s", string(data), err.Error())
		return
	}

	if balanceResp.Data != nil {
		balance := balanceResp.Data.BCredit
		balance = strings.Replace(balance, ",", "", -1)
		m.agentInfo.Balance, _ = strconv.ParseFloat(balance, 64)
	}
	logs.Debug("%+v", m.agentInfo)
	return
}

func (m *MaxBet) ReLogin() (reps *models.ActionResp) {
	return m.Login()
}

func (m *MaxBet) Place(order *models.Order) (resp *models.ActionResp) {
	return
}

func (m *MaxBet) CheckLogin() (resp *models.ActionResp) {
	return
}

func (m *MaxBet) GetGames() (games []*models.Game) {
	return
}

func (m *MaxBet) RealTimeDynamic() (gameDataChan <-chan *models.GameData) {
	return
}

func (m *MaxBet) getDefaultHeader() http.Header {
	header := make(http.Header)
	header.Set("User-Agent", m.userAgent)
	return header
}

func (m *MaxBet) errActionResp(err error) (resp *models.ActionResp) {
	resp = new(models.ActionResp)
	if err != nil {
		resp.IsSuccess = false
		resp.Msg = err.Error()
		return
	}

	resp.IsSuccess = true
	return
}
